package com.martini.satoken01.service.system;

import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.martini.satoken01.entity.SysUser;
import com.martini.satoken01.enums.UserStatus;
import com.martini.satoken01.mapper.system.SysUserMapper;
import com.martini.satoken01.model.domain.OnlineInfoDO;
import com.martini.satoken01.mq.constant.DestinationConstant;
import com.martini.satoken01.mq.model.SendEmailEventDO;
import com.martini.satoken01.service.LoginInfoCacheService;
import com.martini.satoken01.service.MessageQueueService;
import com.martini.satoken01.utils.TemplateUtils;
import com.martini.satoken01.utils.VelocityTemplateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author martini at 2021/12/18 23:47
 */
@Slf4j
public class DataSourceUserServiceImpl implements UserService {

    private final SysUserMapper userMapper;

    private final LoginInfoCacheService loginInfoCacheService;

    private final MessageQueueService messageQueueService;

    public DataSourceUserServiceImpl(SysUserMapper userMapper, LoginInfoCacheService loginInfoCacheService, MessageQueueService messageQueueService) {
        this.userMapper = userMapper;
        this.loginInfoCacheService = loginInfoCacheService;
        this.messageQueueService = messageQueueService;
    }

    @Override
    public SysUser findUserByUsername(String username) {
        Example example = new Example(SysUser.class);
        example.createCriteria().andEqualTo("username", username);
        return userMapper.selectOneByExample(example);
    }

    @Override
    public boolean addUser(SysUser sysUser) {
        SysUser existsUser = this.findUserByUsername(sysUser.getUsername());
        if (existsUser != null) {
            return false;
        }
        String salt = BCrypt.gensalt(10);
        String hashpw = BCrypt.hashpw(sysUser.getPassword(), salt);

        sysUser.setStatus(UserStatus.VALIDATION.getCode());
        sysUser.setRegisterTime(LocalDateTime.now());
        sysUser.setSalt(salt);
        sysUser.setPassword(hashpw);
        int updated = userMapper.insert(sysUser);
        return updated > 0;
    }

    @Override
    public SysUser findUserByEmail(String email) {
        Example example = new Example(SysUser.class);
        example.createCriteria().andEqualTo("email", email);
        return userMapper.selectOneByExample(example);
    }

    @Override
    public SysUser findUserById(long id) {
        Example example = new Example(SysUser.class);
        example.createCriteria().andEqualTo("id", id);
        return userMapper.selectOneByExample(example);
    }

    @Override
    public boolean sendValidationEmail(long loginId) {
        Example example = new Example(SysUser.class);
        example.createCriteria().andEqualTo("id", loginId);
        SysUser sysUser = userMapper.selectOneByExample(example);
        if (StringUtils.isBlank(sysUser.getEmail())) {
            return false;
        }

        Map<String, Object> data = new HashMap<>(4);
        // 6位验证码
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            sb.append(Math.abs(random.nextInt()) % 10);
        }
        String captcha = sb.toString();
        data.put("captcha", captcha);
        // 二维码生成
        QrConfig qrConfig = new QrConfig();
        qrConfig.setWidth(250);
        qrConfig.setHeight(250);
        String qrcode = QrCodeUtil.generateAsBase64("https://www.baidu.com/s?wd=" + sb.toString(), qrConfig, "jpeg");
        data.put("qrcode", qrcode);
        // 生成时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        data.put("generateTime", formatter.format(LocalDateTime.now()));
        // 用户名
        data.put("nickname", sysUser.getNickname());

        // 缓存
        String key = UserService.getEmailValidationKey(sysUser.getId());
        loginInfoCacheService.put(key, captcha, 300000, TimeUnit.MILLISECONDS);

        TemplateUtils templateUtils = new VelocityTemplateUtils();
        String text = templateUtils.renderTemplate("templates/mail", "captcha.vm", data);
        SendEmailEventDO emailEventDO = new SendEmailEventDO()
            .setFrom("tcseeker@aliyun.com")
            .setTo(sysUser.getEmail())
            .setSubject("邮箱验证码")
            .setText(text)
            .setHtml(true);
        messageQueueService.sendOneWay(DestinationConstant.EMAIL_DESTINATION, emailEventDO);

        return true;
    }

    @Override
    public boolean checkEmailValidationCode(long loginId, String code) {
        String cacheCode = loginInfoCacheService.get(UserService.getEmailValidationKey(loginId), String.class);
        if (!Objects.equals(cacheCode, code)) {
            return false;
        }
        SysUser sysUser = new SysUser()
            .setId(loginId)
            .setStatus(UserStatus.ACTIVE.getCode());
        int updated = userMapper.updateByPrimaryKeySelective(sysUser);
        return updated > 0;
    }

    @Override
    public boolean updateByPrimaryKeySelective(SysUser sysUser) {
        return userMapper.updateByPrimaryKeySelective(sysUser) > 0;
    }

    @Override
    public boolean online(OnlineInfoDO onlineInfoDO) {
        String infoKey = UserService.getOnlineInfoKey(onlineInfoDO.getLoginId());
        List<OnlineInfoDO> onlineInfos = loginInfoCacheService.listRange(infoKey, 0, -1, OnlineInfoDO.class);
        if (onlineInfos == null) {
            // 原先没有在线信息
            loginInfoCacheService.listLeftPush(infoKey, onlineInfoDO);
        } else {
            // 遍历有没有此设备的在线信息
            boolean sameDeviceOnline = false;
            for (OnlineInfoDO info : onlineInfos) {
                if (Objects.equals(info.getLoginId(), onlineInfoDO.getLoginId())
                    && Objects.equals(info.getDevice(), onlineInfoDO.getDevice())) {
                    sameDeviceOnline = true;
                    break;
                }
            }
            if (!sameDeviceOnline) {
                loginInfoCacheService.listLeftPush(infoKey, onlineInfoDO);
            }
        }
        return true;
    }

    @Override
    public List<OnlineInfoDO> getOnlineInfo(Long loginId) {
        return loginInfoCacheService.listRange(UserService.getOnlineInfoKey(loginId), 0, -1, OnlineInfoDO.class);
    }

    @Override
    public boolean offline(Long loginId, String device) {
        String infoKey = UserService.getOnlineInfoKey(loginId);
        List<OnlineInfoDO> onlineInfos = loginInfoCacheService.listRange(infoKey, 0, -1, OnlineInfoDO.class);
        if (onlineInfos != null) {
            for (OnlineInfoDO onlineInfo : onlineInfos) {
                if (Objects.equals(onlineInfo.getLoginId(), loginId)
                    && Objects.equals(onlineInfo.getDevice(), device)) {
                    this.loginInfoCacheService.listRemove(infoKey, 1, onlineInfo);
                    break;
                }
            }
        }
        return true;
    }

}
